package com.gitee.luosl.cola.stream;

import com.gitee.luosl.cola.lang.function.checked.CheckedBiConsumer;
import com.gitee.luosl.cola.lang.function.checked.CheckedBiFunction;
import com.gitee.luosl.cola.lang.function.checked.CheckedBiPredicate;
import com.gitee.luosl.cola.lang.function.checked.CheckedFunction;
import com.gitee.luosl.cola.match.Match;
import com.gitee.luosl.cola.lang.Tuple;
import com.gitee.luosl.cola.lang.Tuple2;
import com.gitee.luosl.cola.match.cases.ICase;

import java.util.*;
import java.util.function.*;
import java.util.stream.*;

public final class EntryStreamWrapper<K, V> extends AbstractStreamWrapper<Tuple2<K, V>> {

    EntryStreamWrapper(Stream<Tuple2<K, V>> stream) {
        super(stream);
    }

    private <K1, V1> EntryStreamWrapper<K1, V1> wrap(Stream<Tuple2<K1, V1>> stream) {
        return new EntryStreamWrapper<>(stream);
    }

    public EntryStreamWrapper<K, V> parallel(){
        return wrap(getStream().parallel());
    }

    public GeneralStreamWrapper<Tuple2<K, V>> toGeneral(){
        return StreamWrapper.wrap(getStream());
    }

    public EntryStreamWrapper<K, V> filter(BiPredicate<K, V> predicate) {
        return wrap(getStream().filter(tuple -> tuple.test(predicate)));
    }

    public EntryStreamWrapper<K, V> filterTry(CheckedBiPredicate<K, V> predicate) {
        return filter(predicate.toBiPredicate());
    }

    public <K1, V1> EntryStreamWrapper<K1, V1> map(BiFunction<K, V, Tuple2<K1, V1>> mapper) {
        return wrap(getStream().map(tuple -> tuple.map(mapper)));
    }

    public <K1, V1> EntryStreamWrapper<K1, V1> map(Function<K, K1> keyMapper, Function<V, V1> valueMapper) {
        return map((k, v) -> Tuple.of(keyMapper.apply(k), valueMapper.apply(v)));
    }

    public <K1, V1> EntryStreamWrapper<K1, V1> mapTry(CheckedBiFunction<K, V, Tuple2<K1, V1>> mapper) {
        return wrap(getStream().map(tuple -> tuple.map(mapper.toBiFunction())));
    }

    public <K1, V1> EntryStreamWrapper<K1, V1> mapTry(CheckedFunction<K, K1> keyMapper, CheckedFunction<V, V1> valueMapper) {
        return mapTry((k, v) -> Tuple.of(keyMapper.apply(k), valueMapper.apply(v)));
    }

    @SafeVarargs
    public final <U, K1, V1> EntryStreamWrapper<K1, V1> map(ICase<? extends Tuple2<K, V>, ? extends U, ? extends Tuple2<K1, V1>>... cases) {
        return wrap(getStream().map(t -> Match.of(t).matchCatch(cases)));
    }

    public IntStream mapToInt(BiFunction<K, V, Integer> mapper){
        return getStream().mapToInt(tuple -> tuple.apply(mapper));
    }

    public IntStream mapToIntTry(CheckedBiFunction<K, V, Integer> mapper){
        return mapToInt(mapper.toBiFunction());
    }

    public LongStream mapToLong(BiFunction<K, V, Long> mapper){
        return getStream().mapToLong(tuple -> tuple.apply(mapper));
    }

    public LongStream mapToLongTry(CheckedBiFunction<K, V, Long> mapper){
        return mapToLong(mapper.toBiFunction());
    }

    public DoubleStream mapToDouble(BiFunction<K, V, Double> mapper){
        return getStream().mapToDouble(tuple -> tuple.apply(mapper));
    }

    public DoubleStream mapToDoubleTry(CheckedBiFunction<K, V, Double> mapper){
        return mapToDouble(mapper.toBiFunction());
    }

    public <R> GeneralStreamWrapper<R> mapToGeneral(BiFunction<K, V, R> mapper) {
        return StreamWrapper.wrap(getStream().map(t -> t.apply(mapper)));
    }

    public <R> GeneralStreamWrapper<R> mapToGeneralTry(CheckedBiFunction<K, V, R> mapper) {
        return mapToGeneral(mapper.toBiFunction());
    }

    @SafeVarargs
    public final <U, R> GeneralStreamWrapper<R> mapToGeneral(ICase<? extends Tuple2<K, V>, ? extends U, ? extends R>... cases) {
        return StreamWrapper.wrap(getStream().map(t -> Match.of(t).matchCatch(cases)));
    }

    public <R> EntryStreamWrapper<R, V> mapKeys(Function<K, R> mapper) {
        return wrap(getStream().map(tuple -> tuple.map1(mapper)));
    }

    public <R> EntryStreamWrapper<R, V> mapKeysTry(CheckedFunction<K, R> mapper) {
        return mapKeys(mapper.toFunction());
    }

    @SafeVarargs
    public final <U, R> EntryStreamWrapper<R, V> mapKeys(ICase<? extends K, ? extends U, ? extends R>... cases) {
        return mapKeys(k -> Match.of(k).matchCatch(cases));
    }

    public <R> EntryStreamWrapper<K, R> mapValues(Function<V, R> mapper) {
        return wrap(getStream().map(tuple -> tuple.map2(mapper)));
    }

    public <R> EntryStreamWrapper<K, R> mapValuesTry(CheckedFunction<V, R> mapper) {
        return mapValues(mapper.toFunction());
    }

    @SafeVarargs
    public final <U, R> EntryStreamWrapper<K, R> mapValues(ICase<? extends V, ? extends U, ? extends R>... cases) {
        return mapValues(v -> Match.of(v).matchCatch(cases));
    }

    public GeneralStreamWrapper<K> keyStreamWrapper(){
        return mapToGeneral((k, v) -> k);
    }

    public GeneralStreamWrapper<V> valueStreamWrapper(){
        return mapToGeneral((k, v) -> v);
    }

    public <K1, V1> EntryStreamWrapper<K1, V1> flatMap(BiFunction<K, V, EntryStreamWrapper<K1, V1>> mapper) {
        return wrap(getStream().flatMap(t -> t.apply(mapper).getStream()));
    }

    public <K1, V1> EntryStreamWrapper<K1, V1> flatMapTry(CheckedBiFunction<K, V, EntryStreamWrapper<K1, V1>> mapper) {
        return flatMap(mapper.toBiFunction());
    }

    @SafeVarargs
    public final <U, K1, V1> EntryStreamWrapper<K1, V1> flatMap(ICase<? extends Tuple2<K, V>, ? extends U, ? extends EntryStreamWrapper<K1, V1>>... cases) {
        return wrap(getStream().flatMap(tuple -> Match.of(tuple).matchCatch(cases).getStream()));
    }

    public void forEach(BiConsumer<K, V> action) {
        forEach(tuple -> tuple.accept(action));
    }

    public void forEachOrdered(BiConsumer<K, V> action) {
        forEachOrdered(tuple -> tuple.accept(action));
    }

    public EntryStreamWrapper<K, V> distinct() {
        return wrap(getStream().distinct());
    }

    public EntryStreamWrapper<K, V> sorted() {
        return wrap(getStream().sorted());
    }

    public EntryStreamWrapper<K, V> sorted(Comparator<? super Tuple2<K, V>> comparator) {
        return wrap(getStream().sorted(comparator));
    }

    public EntryStreamWrapper<K, V> peek(BiConsumer<K, V> action) {
        return wrap(getStream().peek(tuple -> tuple.accept(action)));
    }

    public EntryStreamWrapper<K, V> peekTry(CheckedBiConsumer<K, V> action) {
        return peek(action.toConsumer());
    }

    public EntryStreamWrapper<K, V> limit(long maxSize) {
        return wrap(getStream().limit(maxSize));
    }

    public EntryStreamWrapper<K, V> skip(long n) {
        return wrap(getStream().skip(n));
    }

    public Map<K, V> toMap(){
        return toMap(Tuple2::get_1, Tuple2::get_2);
    }

    public LinkedHashMap<K, V> toLinkedHashMap(){
        return (LinkedHashMap<K, V>) toMap(Tuple2::get_1, Tuple2::get_2, (x, y) -> y, LinkedHashMap::new);
    }

    private EntryStreamWrapper<K, V> sortByAsc(Supplier<Comparator<Tuple2<K, V>>> supplier, boolean nullFirst) {
        Comparator<Tuple2<K, V>> comparator = supplier.get();
        if(nullFirst){
            comparator = Comparator.nullsFirst(comparator);
        }else{
            comparator = Comparator.nullsLast(comparator);
        }
        return sorted(comparator);
    }

    public <R extends Comparable<R>> EntryStreamWrapper<K, V> sortByAsc(BiFunction<K, V, R> orderExtractor){
        return sortByAsc(orderExtractor, true);
    }

    public <R extends Comparable<R>> EntryStreamWrapper<K, V> sortByAsc(BiFunction<K, V, R> orderExtractor, boolean nullFirst) {
        Function<Tuple2<K, V>, R> extractor = tuple -> tuple.apply(orderExtractor);
        return sortByAsc(() -> Comparator.comparing(extractor), nullFirst);
    }

    public <R> EntryStreamWrapper<K, V> sortByAsc(BiFunction<K, V, R> orderExtractor, Comparator<R> byComparator){
        return sortByAsc(orderExtractor, byComparator, true);
    }

    public <R> EntryStreamWrapper<K, V> sortByAsc(BiFunction<K, V, R> orderExtractor, Comparator<R> byComparator, boolean nullFirst) {
        Function<Tuple2<K, V>, R> extractor = tuple -> tuple.apply(orderExtractor);
        return sortByAsc(() -> Comparator.comparing(extractor, byComparator), nullFirst);
    }

    public <R extends Comparable<R>> EntryStreamWrapper<K, V> sortByDesc(BiFunction<K, V, R> orderExtractor){
        return sortByDesc(orderExtractor, false);
    }

    public <R extends Comparable<R>> EntryStreamWrapper<K, V> sortByDesc(BiFunction<K, V, R> orderExtractor, boolean nullFirst) {
        Function<Tuple2<K, V>, R> extractor = tuple -> tuple.apply(orderExtractor);
        return sortByAsc(() -> Comparator.comparing(extractor).reversed(), nullFirst);
    }

    public <R> EntryStreamWrapper<K, V> sortByDesc(BiFunction<K, V, R> orderExtractor, Comparator<R> byComparator){
        return sortByDesc(orderExtractor, byComparator, false);
    }

    public <R> EntryStreamWrapper<K, V> sortByDesc(BiFunction<K, V, R> orderExtractor, Comparator<R> byComparator, boolean nullFirst) {
        Function<Tuple2<K, V>, R> extractor = tuple -> tuple.apply(orderExtractor);
        return sortByAsc(() -> Comparator.comparing(extractor, byComparator).reversed(), nullFirst);
    }

    public Map<K, List<V>> groupByKey(){
        return groupBy(Tuple2::get_1, Tuple2::get_2);
    }

    public EntryStreamWrapper<K, List<V>> groupByKeyWrap() {
        return StreamWrapper.wrapEntry(groupByKey());
    }

    public <KEY> EntryStreamWrapper<K, V> distinctBy(BiFunction<K, V, KEY> byExtractor) {
        return distinctBy(t -> t.apply(byExtractor), this::wrap);
    }

    public EntryStreamWrapper<K, V> filterNot(BiPredicate<K, V> predicate){
        return filter(predicate.negate());
    }

    public Map<K, V> reduceByKey(BinaryOperator<V> accumulator) {
        return toMap(Tuple2::get_1, Tuple2::get_2, accumulator);
    }

    public EntryStreamWrapper<K, V> takeWhile(BiPredicate<? super K, ? super V> predicate) {
        return wrap(toGeneral().takeWhile(t -> t.test(predicate)).getStream());
    }
    public EntryStreamWrapper<K, V> dropWhile(BiPredicate<? super K, ? super V> predicate) {
        return wrap(toGeneral().dropWhile(t -> t.test(predicate)).getStream());
    }

    @SafeVarargs
    public final <U> void forEach(ICase<Tuple2<K, V>, ? extends U, ?> ...cases){
        forEach(item -> Match.of(item).matchCatch(cases));
    }

    @SafeVarargs
    public final <U> EntryStreamWrapper<K, V> peek(ICase<Tuple2<K, V>, ? extends U, ?> ...cases) {
        return peek((x, y) -> Match.of(x, y).matchCatch(cases));
    }

    public EntryStreamWrapper<K, V> reWrap(){
        return StreamWrapper.wrapEntry(toList());
    }

}
